package org.vesselonline.mail;

import java.util.Properties;
import java.util.Vector;
import javax.mail.AuthenticationFailedException;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;
import javax.swing.JOptionPane;
import javax.swing.JTable;

public class MailDisplay extends BriefingDisplay {
  /**
   * For Serializable interface, value generated by Eclipse.
   */
  private static final long serialVersionUID = -8379557853393689111L;

  private final MailAccount acct;

  private final Properties props = new Properties();
  private final Session session;
  private Store store;
  private Folder folder;

  private static final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";

  private static final Vector<String> COLUMN_NAMES = new Vector<String>(3);
  static {
    COLUMN_NAMES.add("#");
    COLUMN_NAMES.add("From");
    COLUMN_NAMES.add("Subject");
  }

  public MailDisplay(int tabIndex, MailAccount acct) {
    super(tabIndex, acct.getUsername() + " - " + acct.getFolder(), acct.getDelay(), COLUMN_NAMES);
    this.acct = acct;

    // Set properties object and generate the mail Session
    this.setProperties();
    session = Session.getInstance(props);
  }

  private String inputPassword() {
    return JOptionPane.showInputDialog("Please input your " + acct.getAccount() + " password, " + acct.getName() + ":");
  }

  @Override
  protected JTable createTable() {
    return new JTable(getTableModel());
  }

  protected void refresh() throws DailyBriefingException {
    Vector<String> row;

    // If password has not yet been entered by the user, then prompt
    if (acct.getPassword() == null) acct.setPassword(inputPassword());

    // Get the store for the provider specified
    try {
      store = session.getStore(acct.getProvider());

      // Connect to the store; catch exception and re-prompt for password if authentication fails
      store.connect(acct.getHost(), acct.getUsername(), acct.getPassword());

      // Get folder and retrieve messages
      folder = store.getFolder(acct.getFolder());
      folder.open(Folder.READ_ONLY);
      Message message[] = folder.getMessages();
      int newUnreadMsgCount = 0;

      // Empty the current table data, then reload with retrieved messages
      getTableModel().setRowCount(0);
      for (int i = message.length - 1; i >= 0; i--) {
        row = new Vector<String>(3);
        row.add(0, Integer.toString(i + 1));
        row.add(1, message[i].getFrom()[0].toString());
        row.add(2, message[i].getSubject());
        getTableModel().addRow(row);
        // If message is new since last time folder opened, then highlight.
        // 2nd part of || clause added to support Gmail recent message handling.
        // 2nd part of && clause added to support Exchange/Outlook recent message handling.
        if ((message[i].isSet(Flags.Flag.RECENT) || acct.getHost().equals("imap.gmail.com")) && ! message[i].isSet(Flags.Flag.SEEN)) {
          getTable().addRowSelectionInterval(message.length - (i + 1), message.length - (i + 1));
          newUnreadMsgCount++;
        }
      }

      // Update time and message count labels
      getTimeLabel().setText(DailyBriefingUtils.INSTANCE.getCurrentTime());
      // Replaced implementation using Folder.getNewMessageCount() and Folder.getUnreadMessageCount()
      // due to issues with performance and with Gmail and Exchange/Outlook use of Flags.
      getMessageLabel().setText(folder.getMessageCount() + " messages (" + newUnreadMsgCount + " new/unread)");

      // Close connection
      folder.close(false);
      store.close();

    } catch (NoSuchProviderException nspe) {
      throw new DailyBriefingException(nspe);
    } catch (AuthenticationFailedException afe) {
      acct.setPassword(null);
      throw new DailyBriefingException(afe);
    } catch (MessagingException me) {
      throw new DailyBriefingException(me);
    }
  }

  @Override
  protected void handleException(DailyBriefingException dbe) {
    getMessageLabel().setText(dbe.getCause().getMessage());

    try {
      if (folder != null) folder.close(false);
      if (store != null) store.close();
    } catch (MessagingException me) {
      getMessageLabel().setText(me.getMessage());
    }
  }

  /** Need to override default JavaMail properties for secure connections. */
  private void setProperties() {
    if (acct.isSecure()) {
       if (acct.getProvider().equals("imap")) {
        props.setProperty("mail.imap.socketFactory.class", SSL_FACTORY);
        props.setProperty("mail.imap.socketFactory.fallback", "false");
        props.setProperty("mail.imap.port", "993");
        props.setProperty("mail.imap.socketFactory.port", "993");
      } else if (acct.getProvider().equals("pop3")) {
        props.setProperty("mail.pop3.socketFactory.class", SSL_FACTORY);
        props.setProperty("mail.pop3.socketFactory.fallback", "false");
        props.setProperty("mail.pop3.port", "995");
        props.setProperty("mail.pop3.socketFactory.port", "995");
      }
    } else props.clear();
  }
}
